升級至 .NET 6 時多餘 AsQueryable() 呼叫的原因
TLDR
- 舊版 .NET Core 專案中殘留的
AsQueryable()呼叫,通常是為了處理IQueryable與IAsyncEnumerable擴充方法衝突的問題。 - 當專案同時安裝
System.Linq.Async與舊版 EF Core 時,編譯器因無法判斷Where()或Select()應套用至哪個介面而產生歧義。 - 升級至 EF Core 6 後,
DbSet不再直接實作IAsyncEnumerable,因此可安全移除多餘的AsQueryable()呼叫。
擴充方法衝突的原因分析
什麼情況下會遇到這個問題:當專案同時引用了 System.Linq.Async 套件,且使用 EF Core 5 或更早的版本時。
在 EF Core 5 以前,DbSet<TEntity> 同時實作了 IQueryable<TEntity> 與 IAsyncEnumerable<TEntity> 介面:
csharp
public abstract class DbSet<TEntity> :
Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<IServiceProvider>,
System.Collections.Generic.IAsyncEnumerable<TEntity>,
System.Collections.Generic.IEnumerable<TEntity>,
System.ComponentModel.IListSource,
System.Linq.IQueryable<TEntity> where TEntity : class此時,System.Linq.Async 提供的 AsyncEnumerable 擴充方法與 System.Linq 提供的 Queryable 擴充方法(如 Where、Select)會發生衝突。編譯器無法判斷開發者意圖呼叫的是同步的 IQueryable 擴充方法,還是非同步的 IAsyncEnumerable 擴充方法。為了明確指定型別,開發者必須顯式呼叫 AsQueryable()。
升級至 EF Core 6 的改善
什麼情況下可以移除 AsQueryable():當專案已經升級至 EF Core 6 或更高版本時。
在 EF Core 6 中,DbSet<TEntity> 的介面實作已經調整,移除了對 IAsyncEnumerable<TEntity> 的直接實作:
csharp
public abstract class DbSet<TEntity> :
Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<IServiceProvider>,
System.Collections.Generic.IEnumerable<TEntity>,
System.ComponentModel.IListSource,
System.Linq.IQueryable<TEntity> where TEntity : class由於介面定義的改變,編譯器不再面臨擴充方法選擇的歧義。因此,即便專案中仍安裝有 System.Linq.Async,也不會再發生衝突,開發者可以安全地移除程式碼中多餘的 AsQueryable() 呼叫。
異動歷程
- 2024-07-16 初版文件建立。
